package me.tonywang.security.access;

import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import javax.servlet.ServletException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;

public class ProtectedUrlFilter extends FilterSecurityInterceptor {


    public ProtectedUrlFilter(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
        AccessDecisionVoter voter = new WebExpressionVoter();
        ((WebExpressionVoter) voter).setExpressionHandler(expressionHandler);

        super.setAccessDecisionManager(new AffirmativeBased(Arrays.asList(voter)));
    }

    @Override
    public void invoke(FilterInvocation fi) throws IOException, ServletException {

        final boolean debug = logger.isDebugEnabled();

        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
                .getAttributes(fi);


        Authentication authenticated = SecurityContextHolder.getContext().getAuthentication();

        if (debug) {
            logger.debug("Secure object: " + fi + "; Attributes: " + attributes);
            logger.debug("authenticated: " + authenticated);
        }

        if (attributes != null ) {
            // Attempt authorization
            try {
                super.getAccessDecisionManager().decide(authenticated, fi, attributes);
            } catch (AccessDeniedException accessDeniedException) {
                throw accessDeniedException;
            }
        }

        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    }
}
